﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SNFEGame.Util;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SNFEGame.Visual
{

    /*
     * 
     * (\__/)
     * ( =-.-)   SUP GUISE?
     * C(")(")
     * 
     */

    /// <summary>
    /// Types of horizontal directions in which the character can be moving.
    /// </summary>
    public enum Direction { RIGHT, LEFT, NONE }

    /// <summary>
    /// The class for code involving the character: collision detection, movement, and animation
    /// </summary>
    public class Character : Sprite
    {
        public const string WALKING = "Walking", STANDING = "Standing", JUMPING = "Jumping";

        private static readonly float MAX_X_VELOCITY = 600f;//600
        private static readonly float FRICTION = 64f;//32

        private const float gravity = 1300f;//500

        private Vector2 velocity = Vector2.Zero;

        private Direction currentDirection = Direction.NONE;

        public bool justJumped = false;

        private List<Sprite> touching = new List<Sprite>();
        public List<Sprite> sprites;

        public bool dead = false;
        public bool inJump = false;
        public bool reachedEnd = false;

        public Character(List<Sprite> sprites)
            : base(Storage.characterAnimationMap, STANDING, 0.051f)
        {
            position = new Vector2(MainGame.Width / 2, 0);
            this.sprites = sprites;

            boundingWidth = 32;
            boundingHeight = 64;
        }

        public Character(List<Sprite> sprites, Vector2 position)
            : base(Storage.characterAnimationMap, STANDING, 0.051f)
        {
            this.position = position;
            this.sprites = sprites;
            
            boundingWidth = 32;
            boundingHeight = 64;
        }

        public override void Update(float deltaT)
        {
            // Adjust x component of velocity based on currentDirection
            switch (currentDirection)
            {
                case Direction.LEFT:
                    SwitchAnimation(WALKING);
                    
                    float test1 = velocity.X - FRICTION;
                    if (test1 >= -MAX_X_VELOCITY)
                        velocity.X = test1;
                    break;

                case Direction.RIGHT:
                    SwitchAnimation(WALKING);

                    float test = velocity.X + FRICTION;
                    if (test <= MAX_X_VELOCITY)
                        velocity.X = test;
                    break;

                case Direction.NONE:
                    SwitchAnimation(STANDING);

                    float test2;
                    if (velocity.X < 0)
                    {
                        test2 = velocity.X + FRICTION;
                        if (test2 <= 0)
                            velocity.X = test2;
                    }
                    else
                    {
                        test2 = velocity.X - FRICTION;
                        if (test2 >= 0)
                            velocity.X = test2;
                    }
                    break;
            }

            //Console.WriteLine(velocity.Y);
            if (velocity.Y != 0)
                SwitchAnimation(JUMPING);

            // Euler's Method implementation for updating physics of character
            if (position.Y >= MainGame.Height *2)
            {
                dead = true;
                velocity.X = 0;
            }
            else if (!checkY(deltaT, sprites))
                velocity.Y += gravity * deltaT;
            ///When we move forward, are we touching anything? If not, move forward, baby.
            if (!checkX(deltaT, sprites))
            {
                float oldPosition = position.X;
                position.X += velocity.X * deltaT;
                if (position.X < oldPosition)
                {
                    if (!base.flip)

                        base.flip = true;
                }
                else if (position.X > oldPosition)
                    if (base.flip)
                        base.flip = false;

                foreach (Sprite m in sprites)
                    if (m is MovingBlock && m.Bounds.Intersects(Bounds))
                    {
                        position.X += (m as MovingBlock).velocity.X * deltaT * 1.2f;
                        break;
                    }
                    else
                        if (m is DisappearingBlock && m.Bounds.Intersects(Bounds) && !(m as DisappearingBlock).pop)
                        {

                            (m as DisappearingBlock).beginCountDown(sprites);
                            break;
                        }

            }
            ///When we move upward (or downward, for that matter), are we touching anything? If not, then rock the boat, baby.
            if (!checkY(deltaT, sprites))
            {
                //Console.WriteLine("Adding " + velocity.Y * deltaT + " to position.");
                position.Y += velocity.Y * deltaT;
            }
            else
            {
                ///Oh, so we can't move up or down? Then stop, because you just hit a brick wall... baby.
                velocity.Y = 0f;
            }

            //if (justJumped)
            //    justJumped = false;
            //Console.WriteLine(position.Y + " || " + velocity.Y);

            base.Update(deltaT);
        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            Rectangle r = CurrentFrame.Bounds;
            r.X = (int)position.X;
            r.Y = (int)position.Y;

            //spriteBatch.Draw(Storage.getTexture("block"), r, Color.Yellow); 
            base.Draw(spriteBatch);
        }

        /// <summary>
        /// Simulates jumping by adding positive y velocity to the character.
        /// </summary>
        public void jump(float deltaT)
        {
            //Console.WriteLine("At y " + position.Y);
            if (!justJumped)
            {
                inJump = true;
                float value = velocity.Y - 90f;//50
                if (value > -700f)//360
                    velocity.Y = value;
                else
                    justJumped = true;

                SwitchAnimation(JUMPING);
            }
            //}
            if (checkY(deltaT, sprites))
            {
                inJump = false;
                justJumped = false;
                velocity.Y = 0;
            }

        }

        /// <summary>
        /// Changes movement direction.
        /// </summary>
        /// <param name="d"></param>
        public void setMovement(Direction d)
        {
            currentDirection = d;
        }

        /// <summary>
        /// Checks if a sprite is currently touching the character.
        /// </summary>
        /// <param name="sprite"></param>
        /// <returns></returns>
        public bool isTouching(Sprite sprite)
        {
            //return touching.Contains(sprite);
            if (this.Bounds.Intersects(sprite.Bounds))
                return true;
            return false;
        }

        /// <summary>
        /// Adds a sprite to the list of sprites touching the character.
        /// </summary>
        /// <param name="sprite"></param>
        public void addTouchingSprite(Sprite sprite)
        {
            touching.Add(sprite);
        }

        /// <summary>
        /// Removes a sprite from the list of sprites touching the character.
        /// </summary>
        /// <param name="sprite"></param>
        public void removeTouchingSprite(Sprite sprite)
        {
            touching.Remove(sprite);
        }

        /// <summary>
        /// Actions the sprite preforms when touching the specific passed in sprite
        /// </summary>
        /// <param name="sprite"></param>
        public void handleCollision(Sprite sprite)
        {
            /// Checks if the character's collision occured on the left or right sides.
            touching.Add(sprite);
            if (Bounds.Right > sprite.Bounds.Left || Bounds.Left < sprite.Bounds.Right)
            {
                velocity.X = 0;
            }
            else
            {
                velocity.Y = 0;
                position.Y = (int)(sprite.Bounds.Top - CurrentFrame.Height + 1);
            }
        }


        public bool isStanding
        {
            get
            {
                if (velocity.Y == 0)
                    return true;
                return false;
            }
        }



        /// <summary>
        /// Check if the location we plan to move to (our current location + velocity.X times deltaT) is occupied in the X AXIS.
        /// </summary>
        /// <param name="deltaT"></param>
        /// <param name="drawSprites"></param>
        /// <returns></returns>
        private Boolean checkX(float deltaT, List<Sprite> drawSprites)
        {
            float projectedX = Bounds.X + velocity.X * deltaT;
            Rectangle checkX = new Rectangle((int)projectedX, (int)Bounds.Y - 1, Bounds.Width, Bounds.Height);
            foreach (Sprite m in drawSprites)
            {
                if (m.Bounds.Intersects(checkX))
                {
                    if (drawSprites.IndexOf(m) == drawSprites.Count - 1)
                        reachedEnd = true;

                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Check if the location we plan to move to (our current location + velocity.Y times deltaT) is occupied in the X AXIS.
        /// </summary>
        /// <param name="deltaT"></param>
        /// <param name="drawSprites"></param>
        /// <returns></returns>
        private Boolean checkY(float deltaT, List<Sprite> drawSprites)
        {
            float projectedY = Bounds.Y + velocity.Y * deltaT;
            Rectangle checkY = new Rectangle((int)Bounds.X, (int)projectedY, Bounds.Width, Bounds.Height);
            foreach (Sprite m in drawSprites)
            {
                if (m.Bounds.Intersects(checkY))
                {
                    if (drawSprites.IndexOf(m) == drawSprites.Count - 1)
                        reachedEnd = true;
                    
                    return true;
                }
            }
            return false;

        }

        public List<Sprite> collisionSprites
        {
            get { return this.sprites; }

            set { this.sprites = value; }


        }
    }
}
